#!/usr/bin/python3
import json
import hashlib
import binascii
import os

USERDICT = {
    "root": {'name': 'root', 'pass_hash': '0820bba51011c037b935c0fb536e55c85ead96d1dbf155582ed50cc754b0ee663c545a32c090e3d596b4280c44e1e1e9d5222b04635081d4dfa2e14335f74cbc240bb20579314b47740a49988600b60a046c5c319b5461e48e10ab4a4f2ef0f24', 'balance': 0}
}


curr_user = None

LOGOUT = "Logout"
BALANCE = "Show Balance"
WITHDRAW = "Withdraw Cash"
DEPOSITE = "Deposite"
REGISTER = "Register"
EXIT = "Exit"
LOGIN = "Login"
LOGINED_OPS = [LOGOUT, BALANCE, WITHDRAW, DEPOSITE]
NO_LOGIN_OPS = [LOGIN, REGISTER, EXIT]

# directly copy from https://www.vitoshacademy.com/hashing-passwords-in-python/


def hash_password(password):
    """Hash a password for storing."""
    salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
    pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'),
                                  salt, 100000)
    pwdhash = binascii.hexlify(pwdhash)
    return (salt + pwdhash).decode('ascii')


def verify_password(stored_password, provided_password):
    """Verify a stored password against one provided by user"""
    salt = stored_password[:64]
    stored_password = stored_password[64:]
    pwdhash = hashlib.pbkdf2_hmac('sha512',
                                  provided_password.encode('utf-8'),
                                  salt.encode('ascii'),
                                  100000)
    pwdhash = binascii.hexlify(pwdhash).decode('ascii')
    return pwdhash == stored_password


class LoginError(Exception):
    def __init__(self):
        super().__init__("Incorrect Username or Password")


class Account(object):
    def __init__(self, name, password, pass_hash="", balance=None):
        '''
        创建一个账户，用balance的type来决定是否为已注册的用户
        @inv: self.pass_hash = self_old.pass_hash
        @inv: self.name = self_old.name
        @inv: self.balance >= 0
        @inv: self.balance:: int
        @post: Account object or Exception
        '''
        self.name = name
        if balance == None:
            # this accont is a new account
            self.pass_hash = hash_password(password)
            self.balance = 0
        else:
            self.pass_hash = pass_hash
            if type(balance) != int:
                raise TypeError("Balance must be an integet.")
            if balance < 0:
                raise ValueError("Balance must not less than zeor.")
            if not verify_password(pass_hash, password):
                raise LoginError
            self.balance = balance

    def __str__(self):
        return "User: {:>13} Password:{:20.2f}"\
            .format(self.name, float(self.balance / 100))

    def withdraw(self, amount):
        amount = int(amount * 100)
        if amount > self.balance:
            raise ValueError("Not enough balance!")
        if amount < 0:
            raise ValueError("Withdraw must be a positive number!")
        self.balance -= amount

    def deposite(self, amount):
        amount = int(amount * 100)
        if amount < 0:
            raise ValueError("Deposite must be a positive number!")
        self.balance += amount

# print(Account("root", "root").__dict__)


def fetch_user(username, password):
    if username not in USERDICT:
        raise LoginError
    u_data = USERDICT[username]
    return Account(username, password, u_data['pass_hash'], u_data['balance'])


def login():
    global curr_user
    username = input("Username: ")
    password = input("Password: ")
    curr_user = fetch_user(username, password)
    # say hi
    print(str(curr_user))


def getbalance():
    print(str(curr_user))


def withdraw():
    getbalance()
    curr_user.withdraw(float(input("Withdraw Amount: ").strip()))
    getbalance()


def deposite():
    getbalance()
    curr_user.deposite(float(input("Deposite Amount: ").strip()))
    getbalance()


def logout():
    global curr_user
    USERDICT[curr_user.name] = curr_user.__dict__
    curr_user = None


def register():
    global curr_user
    username = input("Username: ")
    password = input("Password: ")
    if username not in USERDICT:
        curr_user = Account(username, password)
        USERDICT[username] = curr_user
    else:
        curr_user = fetch_user(username, password)
    getbalance()


def load_data():
    path = os.path.join(".data")
    if not os.path.isfile(path):
        return
    USERDICT = json.load(open(path, "r"))


def store_data():
    path = os.path.join(".data")
    json.dump(USERDICT, open(path, "w"))


def do_exit():
    store_data()
    exit()


fun_dict = {
    LOGIN: login,
    BALANCE: getbalance,
    WITHDRAW: withdraw,
    DEPOSITE: deposite,
    LOGOUT: logout,
    REGISTER: register,
    EXIT: do_exit
}


def getops():
    if curr_user:
        return LOGINED_OPS
    else:
        return NO_LOGIN_OPS


def handle_ops(op):
    try:
        opname = getops()[int(op.strip()) - 1]
        fun_dict[opname]()
    except IndexError as e:
        return


def print_menu():
    ops = getops()
    menu = ""
    for i, value in enumerate(ops):
        menu += "{}.{:18}".format(i + 1, value)
        if i % 2 == 1:
            menu += "\n"
    print(menu)


if __name__ == "__main__":
    load_data()
    while True:
        try:
            print_menu()
            handle_ops(input("Choose Your Operation: "))
        except (ValueError, LoginError) as e:
            print(e)
        # except Exception as e:
        #     print("Internel Error")
        #     raise e
